﻿// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using System;

using Nova.Parsing;

namespace Nova.CodeDOM
{
    /// <summary>
    /// The common base class of all code annotations, including user comments (derived from <see cref="CommentBase"/>),
    /// code attributes (<see cref="Attribute"/>), compiler directives (derived from <see cref="CompilerDirective"/>), and generated
    /// messages (derived from <see cref="Message"/>).
    /// </summary>
    public abstract class Annotation : CodeObject
    {
        #region /* FIELDS */

        protected AnnotationFlags _annotationFlags;

        #endregion

        #region /* CONSTRUCTORS */

        protected Annotation()
        { }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// Get the annotation in text format.
        /// </summary>
        public virtual string Text
        {
            get { return AsString(); }
            set { throw new Exception("Can't set Text on this type!"); }
        }

        /// <summary>
        /// True if the annotation should be listed at the <see cref="CodeUnit"/> level (for display in an output window).
        /// </summary>
        public virtual bool IsListed
        {
            get { return false; }
        }

        /// <summary>
        /// True if the annotation appears at the end-of-line (should only be true for EOL comments).
        /// </summary>
        public virtual bool IsEOL
        {
            get { return false; }
            set { throw new Exception("Can't set IsEOL on this type!"); }
        }

        #endregion

        #region /* PARSING */

        protected Annotation(Parser parser, CodeObject parent)
            : base(parser, parent)
        { }

        #endregion

        #region /* FORMATTING */

        /// <summary>
        /// Annotation formatting flags.
        /// </summary>
        public AnnotationFlags AnnotationFlags
        {
            get { return _annotationFlags; }
            protected internal set { _annotationFlags = value; }
        }

        protected internal void SetAnnotationFlag(AnnotationFlags flag, bool value)
        {
            if (value)
                _annotationFlags |= flag;
            else
                _annotationFlags &= ~flag;
        }

        /// <summary>
        /// True if this annotation is marked as any type of Infix location.
        /// </summary>
        public bool IsInfix
        {
            get { return (_annotationFlags & AnnotationFlags.InfixMask) != 0; }
            set { SetAnnotationFlag(AnnotationFlags.IsInfix1, value); }
        }

        /// <summary>
        /// True if this annotation is marked as Infix location #1.
        /// </summary>
        public bool IsInfixLocation1
        {
            get { return _annotationFlags.HasFlag(AnnotationFlags.IsInfix1); }
            set { SetAnnotationFlag(AnnotationFlags.IsInfix1, value); }
        }

        /// <summary>
        /// True if this annotation is marked as Infix location #1.
        /// </summary>
        public bool IsInfixLocation2
        {
            get { return _annotationFlags.HasFlag(AnnotationFlags.IsInfix2); }
            set { SetAnnotationFlag(AnnotationFlags.IsInfix2, value); }
        }

        /// <summary>
        /// True if this annotation is marked as Infix location #1.
        /// </summary>
        public bool IsInfixLocation3
        {
            get { return _annotationFlags.HasFlag(AnnotationFlags.IsInfix3); }
            set { SetAnnotationFlag(AnnotationFlags.IsInfix3, value); }
        }

        /// <summary>
        /// True if this annotation appears after the object it's attached to as opposed to before it.
        /// </summary>
        public bool IsPostfix
        {
            get { return _annotationFlags.HasFlag(AnnotationFlags.IsPostfix); }
            set { SetAnnotationFlag(AnnotationFlags.IsPostfix, value); }
        }

        #endregion
    }

    #region /* ANNOTATION FORMATTING FLAGS */

    /// <summary>
    /// Annotation formatting flags.
    /// </summary>
    [Flags]
    public enum AnnotationFlags
    {
        /// <summary>No flags.</summary>
        None       = 0x00,
        /// <summary>Inline status bit field mask - these bits are used to indicate different types of "infix" comments.</summary>
        InfixMask  = 0x03,
        /// <summary>Indicates Infix location #1 - used for inline comments that appear in an otherwise-empty parameter or argument list, or the initialization
        /// section of a 'for' statement.  Also used for EOL comments that appear after the Block start symbol ('{') or an operator, or compiler directives that
        /// occur between a TypeDecl header and any base type list or type constraints.</summary>
        IsInfix1   = 0x01,
        /// <summary>Indicates Infix location #2 - used for inline comments that appear in an otherwise-empty conditional section of a 'for' statement, or
        /// compiler directives that occur between a ConstructorDecl header and any constructor initializer.</summary>
        IsInfix2   = 0x02,
        /// <summary>Indicates Infix location #3 - used for inline comments that appear in an otherwise-empty iteration section of a 'for' statement.</summary>
        IsInfix3   = 0x03,
        /// <summary>Indicates Postfix location - used for regular comments and compiler directives that appear after an object instead of before it.</summary>
        IsPostfix  = 0x04,
        /// <summary>Used for EOL comments to indicate no space after the comment symbol.</summary>
        NoSpace    = 0x08,
        /// <summary>Used by DocComments objects to represent a missing start tag.</summary>
        NoStartTag = 0x10,
        /// <summary>Used by DocComments objects to represent a missing end tag.</summary>
        NoEndTag   = 0x20
    }

    #endregion
}
